#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <ctype.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>

#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <time.h>
#include <pthread.h>
#include <netinet/ip6.h>
#include <stdlib.h>

#include "global.h"
#include "tunnet.h"
#include "common.h"


void * HandleNeighbor(void * args);

int start_controller(struct node_item * np){
    struct sockaddr_in srvAddr,clntAddr;
    unsigned int port;
    int srvSock, *clntSock;
    int addrLength;

    pthread_t nbrThreadID;
    struct ThreadArgs * nbrArgs;
    
        srvSock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(srvSock<0) {
                sys_log(info_log, "socket error(start_controller)\n");
                return -1;
        }

        memset(&srvAddr, 0, sizeof(srvAddr));
        srvAddr.sin_family=AF_INET;
        srvAddr.sin_port=np->controlPort;
        srvAddr.sin_addr.s_addr=htonl(INADDR_ANY);

        while(bind(srvSock, (struct sockaddr* )&srvAddr, sizeof(srvAddr))<0){
                sys_log(info_log, "bind error(controller.c), port%d is busy, sleep for a moment...\n",
			ntohs(np->controlPort));
		sleep(3);
        }
        if (listen(srvSock, MAX_LISTEN) < 0){
                sys_log(info_log, "listen error(Service_Socks)\n");
                return -2;
        }
	sys_log(info_log,"Controller waiting at TCP port:%d ... \n", ntohs(np->controlPort));

        for(;;){
		addrLength=sizeof(clntAddr);
		clntSock=(int*) malloc(sizeof(int));

                *clntSock=accept(srvSock,(struct sockaddr *)&clntAddr,
                                  &addrLength);
                if( *clntSock <0){
                        sys_log(info_log, "accept error(Service_Socks):%d(%s) \n",
				errno,strerror(errno));
                        return -3;
                }

		nbrArgs=malloc(sizeof(struct ThreadArgs));
                //nbrArgs->version=version;
                nbrArgs->clntSock = *clntSock;
		nbrArgs->clntIP=clntAddr.sin_addr.s_addr;

		free(clntSock);
                if (pthread_create(&nbrThreadID, NULL,HandleNeighbor,
                        (void *) nbrArgs) != 0)
                {
                        sys_log(info_log, "Error(server.c) : pthread_create \n");
                        return -4;
                }	
	}// for(;;)
}

void * HandleNeighbor(void * args){
    int client, status,client1;
    unsigned char version;
    unsigned long bytes;

    PDU_HEADER  ph;
   struct node_item * p_nbr;
    int nodeID; 
    in_addr_t clntIP;
    //char OutMsg[256];

    p_nbr=NULL;
    nodeID=-1;
    pthread_detach(pthread_self());
    client = ((struct ThreadArgs *) args) -> clntSock;
    clntIP= ((struct ThreadArgs *) args) ->clntIP;

    free(args);
    
    
    while(1){
	memset(&ph, 0, sizeof(ph));
	bytes=atomic_in(client,&ph,sizeof(ph)) ;

	if(bytes != sizeof(ph)) {
            //debug("Read PDU Header: %d bytes. client closed\n",bytes);
            break ;
        }
	if(ph.version!=VERSION_CTRL) {
		//debug("Read Control version number != %d\n",VERSION_CTRL);
		break;
	}
	debug("Read PDU: command=%d \n", ph.command);
	switch(ph.command){
                case CMD_NODE_REGISTER:
                        status=s_node_register(client);
                        //p_nbr->status=STATUS_UNKNOWN;
                        if(f_debug)neighbor_display(&neighbors);
			 
                break;
		case CMD_CLIENT_HELLO: 
			status=s_authentication_exchange(client,&nodeID);
			if(status <0) {
			    sys_log(info_log,"Client Authentication Error: HandleNeighbor().\n");
			    close(client);
			    return ;	
			}
			//debug("Client Authenticated:neighbor[%d]. \n" , nodeID);
			p_nbr=(struct node_item*)neighbor_lookup_id(&neighbors,nodeID);
			if(p_nbr==NULL){
				debug("But Error: p_nbr==NULL\n");
			}
			else{
				debug("The authenticated client is No.%d\n",p_nbr->nodeID);
				p_nbr->status=STATUS_ACTIVE;
			}
		break;
		case CMD_NODE_UPDATE: 
			if(p_nbr==NULL){
			   s_reply_error(client,CMD_NODE_REJECT);
			   close(client);
			   return ;
			}
			status=s_node_update(client,nodeID);
			p_nbr->status=STATUS_ACTIVE;
			//if(f_debug)neighbor_display(&neighbors);
		break;

                case CMD_NBR_REQUEST:
                        if(p_nbr==NULL){
                           s_reply_error(client,CMD_NBR_REJECT);
                           close(client);
                           return ;
                        }
                        status=s_nbr_request(client,nodeID);
                break;
		case CMD_SRVC_UPDATE: 
			if(p_nbr==NULL){
			   s_reply_error(client,CMD_SRVC_REJECT);
			   close(client);
			   return ;
			}
			s_service_update(client,nodeID);
		break;

		//Route Update is very dangerious, ignore it
		case CMD_ROUTE_UPDATE: 
			if(p_nbr==NULL){
			   s_reply_error(client,CMD_SRVC_REJECT);
			   close(client);
			   return ;
			}
			s_route_update(client,nodeID);
			if(f_debug)neighbor_display(&neighbors);
		break;

		case CMD_ADMIN: 
			if(p_nbr==NULL){
			   s_reply_error(client,CMD_SRVC_REJECT);
			   close(client);
			   return ;
			}
			client1=client;
			status=s_administrator(client1);//,OutMsg);		
			if(status <0){
				close(client);
				debug("CMD_ADMIN Error \n"); 
				return;
			}	
		break;
		default:
			s_reply_error (client,CMD_STAT_ERROR);
		break;
        }//switch()
    } //while(1)
    close(client);
    pthread_exit(NULL);
}

//int s_node_register(int client, long ipAddress);
/* Register a new node
	C->S: CMD_NODE_REGISTER
	+----1----+----1----+-----1----+-----/----+----1----+----/---+-----2-----+---1---+---/----+
	| VERSION | COMMAND | U_LENGTH | UserName | AddrType| IP_ADDR| CTRL_PORT | P_LEN | SECRET |
	+---------+---------+----------+----------+---------+--------+-----------+-------+--------+

	If server accept this new node, it replys CMD_NODE_REGISTER_ACCEPT;
	otherwise, it replys CMD_NODE_REGISTER_DENY;
	S->C: 
	+----1----+----1----+
	| VERSION | COMMAND |
	+---------+---------+
	
*/
int s_node_register(int client)
{
	PDU_HEADER ph;
        unsigned char version, command, u_length,p_length,addrType;

        char userName[MAX_USERNAME], authMethods[MAX_METHODS],password[MAX_USERNAME];
        int bytes, i, addrLen, controlPort;
        char buf[1024];
	in_addr_t ipAddress;

	struct node_item *sp1,*np;
        struct access_list_item4 *acl_item;

        memset(userName,0, sizeof(userName));
        memset(authMethods,0, sizeof(authMethods));

        ATOMIC_IN(client, &u_length,1,"u_length@s_node_register()\n");
        ATOMIC_IN(client, userName, u_length,"userName@s_node_register()\n");
	
	debug("userName=%s \n", userName);
        ATOMIC_IN(client, &addrType,1,"addrType@s_node_register()\n");
	debug("addrType=%d \n", addrType);

        addrLen=addrLength(addrType);

	debug("addr Length=%d \n", addrLen);
        ATOMIC_IN(client, &ipAddress,addrLen, "Error Read ipAddress, s_node_register()\n");

	ATOMIC_IN(client, &controlPort,2, "Error Read ControlPort, s_node_register()\n");

        ATOMIC_IN(client, &p_length,1,"p_length@s_node_register()\n");
	debug("Password Length=%d \n", p_length);

        ATOMIC_IN(client, password,p_length,"Password@s_node_register()\n");
	
	debug("Get password:%s, %d \n", password, p_length);

	//Insert into node list
	 np= neighbor_lookup_byName(&neighbors,(char *)userName);
         if(np){
            sys_log(info_log,"Neighbor %s exists already. s_node_register()\n",
                         userName);
	    ph.version=VERSION_CTRL;
	    //ph.command=CMD_NODE_REGISTER_USER_EXIST;	    
	    ph.command=CMD_NODE_REGISTER_ACCEPT;	    
	    ATOMIC_OUT(client,&ph, sizeof(ph), "Error on out CMD_NODE_REGISTER in s_register()");
	    return 1;
         }
         sp1=(struct node_item*) malloc(sizeof(struct node_item) );
         if(!sp1) {
            	sys_log(info_log,"Malloc error on node_item in s_node_register()\n");
		return -1;
	  }
         sp1->addrType=addrType;
         sp1->ipAddress=ipAddress;  /**** Notice: ipAddress is network address order ***/

         memset(sp1->userName,0,sizeof(sp1->userName));
         strncpy(sp1->userName,userName,strlen(userName));
         sp1->controlPort=controlPort;  /** network address order**/

         memset(sp1->secret,0,sizeof(sp1->secret));
         strncpy(sp1->secret,password,p_length);

         //sp1->sourceNode =0;   // comes from local (i.e neighbor[0])
	 sp1->sourceNode =neighbors.number;  //Node information comes from itself
	 sp1->status=STATUS_UNKNOWN;

         sp1->services.first=NULL;
         sp1->services.number=0;
         sp1->next=NULL;

         neighbor_insert(&neighbors,sp1);
         //neighbor_display(&neighbors);
         neighbor_dump(&neighbors);
         acl_item=(struct access_list_item4 *) new_acl4(ipAddress,32,ACTION_PERMIT);
         if(acl_item!=NULL)
	         acl_insert4(&acl4, acl_item);
	 ph.version=VERSION_CTRL;
	 ph.command=CMD_NODE_REGISTER_ACCEPT;	    

	 ATOMIC_OUT(client,&ph, sizeof(ph), "Error on out CMD_NODE_REGISTER in s_register()");
	 return 0;

}


/*
  C->S : CMD_CLIENT_HELLO 
        +--------+--------+--------+----/-------+--------+------/-----+
        |VERSION |COMMAND |U_LENGTH| UserName   |NMETHODS|AUTH_METHODS|
        +--------+--------+--------+----/-------+--------+------------+
           1        1         1      U_LENGTH     1         NMETHODS
  S->C: CMD_SERVER_HELLO
	+--------+--------+--------+
        |VERSION |COMMAND | S_A_M  |
        +--------+--------+--------+
            1        1         1      
	or( if S_A_M equal METHOD_CHAP or METHOD_X509):
	
        +--------+--------+--------+---------+------/-----+
        |VERSION |COMMAND | S_A_M  |C_LENGTH |  CHALLENGE |
        +--------+--------+--------+---------+------/-----+
            1        1         1        1        C_LENGTH

  C->S: CMD_CLIENT_AUTH
		   
        +--------+--------+--------+---/------+
        |VERSION |COMMAND | P_LEN  | PASSWORD |
        +--------+--------+--------+---/------+
	    1        1        1        1

  S->C: CMD_SERVER_CONF 
   
        +--------+--------+--------+---/------+
        |VERSION |COMMAND | R_LEN  | RESPONSE |
        +--------+--------+--------+---/------+
            1         1       1        R_LEN  

*/

int s_authentication_exchange(int client, int * p_nbr){
	//VERSION |COMMAND |U_LENGTH| UserName   |NMETHODS|AUTH_METHODS|
	PDU_HEADER ph;
	unsigned char version, command, u_length, n_methods;
	char userName[MAX_USERNAME], authMethods[MAX_METHODS],password[MAX_USERNAME];
	int bytes, i, succeed;
	char buf[1024];
	struct node_item * np;

	memset(userName,0, sizeof(userName));
	memset(authMethods,0, sizeof(authMethods));

	ATOMIC_IN(client, &u_length,1,"u_length@s_authentication_exchange()\n");
	ATOMIC_IN(client, userName, u_length,"userName@s_authentication_exchange()\n");

	ATOMIC_IN(client,&n_methods, 1, "Error read N_METHODS in s_authentication_exchange()\n");

	ATOMIC_IN(client , authMethods, n_methods, "Erorr read Methods in s_authentication_exchange()\n");
	np= neighbor_lookup_byName(&neighbors,(char *)userName);

	if(!np){
		sys_log(info_log,"neighbor lookup failed ,no such user. s_authentication()\n"); 

		// No such User, reply Error 
		s_reply_error(client, CMD_SERVER_NOUSER);
		return -1;
	}

	//Now, Reply the client

	ph.version=VERSION_CTRL;
	ph.command=CMD_SERVER_HELLO;

	ATOMIC_OUT(client,&ph, sizeof(ph), "Erorr reply pdu header in s_authentication_exchange()\n");	

	switch(authMethod){   // authMethod is assigned by comand options -a <methods>, see server.c

	    case METHOD_NONE:
		 buf[0]=METHOD_NONE;
		 bytes=atomic_out(client, buf,1);
		 if(bytes != 1){
		    debug("Error reply method %d bytes sent. s_authentication_exchange()\n",bytes);
		    return -1;
	         }
		 succeed=1;         
	    break;
            case METHOD_PASS:
                 buf[0]=METHOD_PASS;

		 //ATOMIC_OUT(client,buf, 1, "Error reply method %d bytes sent. s_authentication_exchange()\n");
                 bytes=atomic_out(client, buf,1);
                 if(bytes != 1){
                    debug("Error reply method %d bytes sent. s_authentication_exchange()\n",bytes);
                    return -1;
                 }

		// read  pdh header and secret
		bytes=atomic_in(client, &ph,sizeof(ph));
		if(bytes != sizeof(ph)){
		    debug("Error read CLIENT_AUTH Header error. s_authentication_exchange().\n");
		    return -1;
		}

		if(ph.command !=CMD_CLIENT_AUTH){
		    debug("CMD_CLIENT_AUTH  error. s_authentication_exchange().\n");
		    s_reply_error(client , CMD_STAT_ERROR);
		    return -1;
		}
		bytes=atomic_in(client, &u_length, 1); // u_length= length of password 
		if(bytes != 1){
                    debug("Error read length of password. s_authentication_exchange().\n");
		    //s_reply_error();
                    return -1;
                }
		bytes=atomic_in(client, password, u_length);
		if(bytes != u_length){
                    debug("Error on read  password. s_authentication_exchange().\n");
		    //s_reply_error();
                    return -1;
                }
		if( strncmp(password, np->secret,u_length) ){ //failure
		    debug("Authentication failure :got:(%s) , expect:(%s)\n", 
			password,np->secret);
		    s_reply_error(client,CMD_SERVER_ERROR); 
		    succeed=0;
		}
		else{
		    debug("Authentication OK :got:(%s) , expect:(%s)\n", 
			password,np->secret);
		    s_reply_error(client,CMD_SERVER_CONF); 
                    succeed=1;
		}

            break;

	}// switch

	//Modify the status of this neighbor 

	if(succeed){
		np->status=STATUS_AUTHED;
		*p_nbr=np->nodeID;
		sys_log(info_log, "Neighbor[%d] connected and authenticated.\n", np->nodeID);
		return 0;
	}
	else return -1;
}

/****************** NODE UPDATE ******************
                 
  C->S: CMD_NODE_UPDATE
        +--------+--------+--------+-----/----+--------+----/-----+
        |VERSION |COMMAND |U_LEN   |USERNAME  |addrType|ipAddress |
        +--------+--------+--------+-----/----+--------+----/-----+
            1        1         1      U_LEN       1        4 or 16     
        ----------+-----------+    
        CTRL_PORT |addrType(0)|
        ----------+-----------+
             2         1
//Notice, please: the ipAddress maybe a NATed ip , or faked delibrately.
//ipAddress should be replaced by the one from getpeername;
    
  S->C: CMD_NODE_ACCEPT
        +--------+--------+--------+-----/----+--------+----/-----+
        |VERSION |COMMAND |U_LEN   |USERNAME  |addrType|ipAddress |
        +--------+--------+--------+-----/----+--------+----/-----+
            1        1         1      U_LEN       1      4 or 16     
        ----------+-----------+    
        CTRL_PORT |addrType(0)|
        ----------+-----------+
             2         1
    
  S->C: CMD_NODE_REJECT
		 	 
        0         8       16
        +--------+--------+
        |VERSION |COMMAND |
        +--------+--------+
****************************************************/
int s_node_update(int client,int ndID){

    PDU_HEADER ph;
    unsigned char version, command, u_length, addrType,tail;
    unsigned int controlPort;
    char ipAddress[128];
    char strIP[128];
    int bytes, i, nodeID;
    struct node_item * me, *peer;
    int  addrLen,length;
    char pdu[1024], *ptr,*userName, inUserName[MAX_USERNAME];

    struct in_addr remoteIP;
    struct in_addr claimIP;
    int remotePort;
    char   strClaimIP[128], strRealIP[128];
    int result;
	

	if(client <0 || ndID <0) return -1;
	if(NULL==(peer=neighbor_lookup_id(&neighbors,ndID) ))
		return -1;

	debug("Begin s_node_update, Now Read node information of the remote client..\n");
        memset(inUserName,0, sizeof(inUserName));

	ATOMIC_IN(client, &u_length,1, "u_length");

	ATOMIC_IN(client, inUserName,u_length, "Error Read inUserName, s_node_update()\n");
	ATOMIC_IN(client, &addrType,1, "Error Read addrType, s_node_update()\n");

	addrLen=addrLength(addrType);

	ATOMIC_IN(client, ipAddress,addrLen, "Error Read ipAddress, s_node_update()\n");

	memcpy((void *)&claimIP, (void *)ipAddress, addrLen);

        strcpy(strClaimIP, (char *)inet_ntoa(claimIP.s_addr) );
	
	debug("The peer claim that it's ip is : %s \n", strClaimIP);

	//ipAddress should be checked:  Is it a  private address ,such as 192.168.x.x ? 

	result=get_remote_socket_info(client, &remoteIP, &remotePort);

        strcpy(strRealIP, (char *)inet_ntoa(remoteIP.s_addr) );

	debug("The peer claim that it's ip is : %s, getpeername got:%s \n", strClaimIP, strRealIP);

	ATOMIC_IN(client, &controlPort,2, "Error Read ControlPort, s_node_update()\n");

	ATOMIC_IN(client, &tail,1, "Error Read Tail, s_node_update()\n");

	debug("Server have got update from client\n");

        // update neighbors infor
	//neighbor_update(ndID,inUserName,addrType,ipAddress, controlPort);
	// replace the  ip address the one get from getpeername:

	neighbor_update(ndID,inUserName,addrType,(char*)&remoteIP.s_addr, controlPort);


	if( strcmp(strClaimIP,strRealIP) )
        {	
		//setNodeUnderFirewall(&neighbors,ndID,1);
		 peer->underFirewall=1;
		 debug("the remote peer is under firewall \n");

		//At the service update stage, the node outside firewall will setup a reverse connection to the node nodeID
	}


	debug("Begin s_node_update, Now Send my node information to the remote client..\n");

        me=neighbors.first;
        memset(pdu,0,sizeof(pdu));
        ph.version=VERSION_CTRL;
        ph.command=CMD_NODE_ACCEPT;

        bytes=atomic_out(client,&ph, sizeof(ph));
        if(bytes != sizeof(ph)){
                debug("send pdu header error  in node_update()\n");
                return -1;
        }

        ptr=pdu; length=0;
        userName=me->userName;
        addrType=me->addrType;

        *ptr=(unsigned char)strlen(userName);
        ptr++;
        strcpy(ptr,userName);
        ptr+=strlen(userName);

        *ptr=me->addrType;
        ptr++;
        addrLen=addrLength(me->addrType);

        memcpy(ptr,(void *)&me->ipAddress,addrLen);

        ptr+=addrLen;

        memcpy(ptr,(void *)&me->controlPort,2);
        ptr+=2;
        *ptr=0;

        ptr++;

        length=ptr-pdu;
        bytes=atomic_out(client, pdu,(ptr-pdu));
        if(bytes != length) {
                debug(" sent node update Error . node_update()\n");
                return -1;
        }
	debug("Server have sent update to client\n");

	return 0;

}
/**********************************************************
Service Update PDU:


        +--------+--------+--------+---------+---------+---------+~~
        |VERSION |COMMAND |srvcType|relayPort|srvcType |relayPort|~
        +--------+--------+--------+---------+---------+---------+~~
           1         1         1       1         4 or 16     2

        ~--------+---------+-----------+
         srvcType|relayPort|srvcType(0)|
        ~--------+---------+-----------+
             1       1        4 or 16

**********************************************************/
int s_service_update(int sock,int nodeID){
	
    PDU_HEADER ph;
    unsigned char  n_services, srvcType,tail;
    int bytes, length;
    unsigned short relayPort;
    struct node_item * me, *peer;
    char pdu[1024],*ptr;
    unsigned int port_com;
    unsigned int port_local;
    int result;
    
    struct service_item * sp;
	
	debug("begin service update\n");


	if(nodeID <0){
		sys_log(info_log, "Error: nodeID <0\n");
		return -1;
	}
	peer=neighbor_lookup_id(&neighbors, nodeID);
	if (!peer) return -1;

	bytes=atomic_in(sock,&srvcType,1);
	if(bytes !=1) { sys_log(info_log, "read srvcType error. s_service_update()\n");return -1;}
	while(srvcType){
		bytes=atomic_in(sock,&relayPort,2);
		if(bytes !=2) { 
		   sys_log(info_log, "read relayPort error. s_service_update()\n");
		   return -1;
                } 
		debug("service_update got :nodeID=%d , srvcType=%d, relayPort=%d\n",
			nodeID,srvcType,relayPort);
		service_update_nbr(nodeID,srvcType,relayPort);

		bytes=atomic_in(sock,&srvcType,1);
		if(bytes !=1) { 
		   sys_log(info_log, "read srvcType error. s_service_update()\n");
		   return -1;
                } 
	}	
	debug("I have got service update from client.\n");

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

    	me=neighbors.first;
    	sp=me->services.first;
    	n_services=me->services.number;

    	//if(n_services<=0) return 0;
	
    	memset(pdu,0,sizeof(pdu));
    	ptr=pdu;
    	length=0;
        
    	while(sp){
		/*
		if(sp->status !=SRVC_STAT_START) {
        		sp=sp->next;
			continue ;
		}
		*/
        	*ptr=sp->srvcType;
        	ptr++;
        	memcpy(ptr,(void*)&(sp->relayPort),2);
        	ptr+=2;
        	sp=sp->next;
    	}
    	*ptr=0;
     	ptr++;

	//if nodeID is NAT, send CMD_SRVC_REVERSE and the communicatio port
	if(peer->underFirewall)
	{
		//Suppose the peer has only one service :
		if(ntohs(peer->services.first->relayPort) < (65534-PORT_REVERSE_BASE))
		{
			port_local = PORT_REVERSE_BASE+ ntohs(peer->services.first->relayPort) + peer->nodeID;
		}
		else {
			port_local = htons(PORT_REVERSE_BASE +  peer->nodeID);
		}
		port_com= port_local+1; 
		port_local=htons(port_local);
		port_com=htons(port_com);
		memcpy(ptr,(void *)&port_com, 2); 
		ptr +=2;
	}

	/*****************************+-------+-------+-------------+ **
 	  if n_service =0, the pdu is |VERSION|COMMAND|SRVCTYPE(0)  |
	******************************+-------+-------+-------------+ */

    	length=ptr-pdu;

    	ph.version=VERSION_CTRL;

	if(peer->underFirewall )    // is NAT, send CMD_SRVC_REVERSE//
	{
		ph.command=CMD_SRVC_REVERSE;

		result=reverse_connect_outer(peer->ipAddress,port_com, port_local); // network byte order
		if(result !=-1)
		{
		    //TO DO:
		    //Modify the remote ip to "127.0.0.1", service port to port_local;	
		    peer->ipAddress=inet_addr("127.0.0.1");
		    //Suppose the peer has only one service :
		    peer->services.first->relayPort=port_local;
		    //debug("Listen on local port : %d\n", ntohs(port_local));
		    neighbor_display(&neighbors);
		}
	}
    	else{
		ph.command=CMD_SRVC_ACCEPT;
		result=1;
	}
    	bytes=atomic_out(sock,&ph, sizeof(ph));
    	if(bytes != sizeof(ph)){
           	debug("send pdu header error  in service_update()\n");
           	return -1;
    	}
    
    	bytes=atomic_out(sock, pdu, length);
    	if(bytes != length){
           debug("send pdu  error  in service_update()\n");
           return -1;
    	}

	return 0;
}
/***********************************************************


C->S: CMD_NBR_REQUEST

     +--------+--------+--------+-----/----+
     |VERSION |COMMAND |U_LEN   |USERNAME  |
     +--------+--------+--------+-----/----+
         1        1         1      U_LEN   
S->: CMD_NBR_REQUEST
            
     +--------+---------+----------+---/------+--------+-----/----+----------+------+---------+
     |VERSION |COMMAND  |  U_LEN   |USERNAME  |addrType|ipAddress |CTRL_PORT |STATUS|U_LEN(0) |
     +--------+---------+----------+---/------+--------+----------+----------+------+---------+
        1         1         1          U_LEN      1       4/16        2          1 



************************************************************/
int s_nbr_request(int sock , int nodeID){
	
    PDU_HEADER ph;
    int bytes;
    unsigned char u_len;
    struct node_item * np,*me;
    char pdu[1024],*ptr, userName[MAX_USERNAME];


        u_len=0;
	memset(userName, 0, sizeof(userName));

        if(nodeID <0){
                sys_log(info_log, "Error: nodeID <0\n");
                return -1;
        }       
        bytes=atomic_in(sock,&u_len,1);
	if(u_len!=0){
		bytes=atomic_in(sock,userName,u_len);
	}

	// read over, now reply ...
	
	ph.version=VERSION_CTRL;
	ph.command=CMD_NBR_RESPONSE;
	
	bytes=atomic_out(sock, &ph, sizeof(ph));	
	if(bytes != sizeof(ph)) { 
		debug("error send response header :%d bytes send.\n",bytes);
		return -1;
	}

	me=neighbors.first;
	np=me->next;
	//np=me->next;
	
	ptr=pdu;
	while(np){
		if(np->sourceNode == nodeID ||  np->sourceNode!=0){ 
			debug("skiped nodeID=%d, sourceNode=%d\n", nodeID,np->sourceNode);
			np=np->next;    
				// if this node comes from this client, 
				// or is not the direct neighbor,skip it..
			continue;
		}
		u_len=strlen(np->userName);
		*ptr++=u_len;

		memcpy(ptr,np->userName,u_len);
		ptr+=u_len;

		*ptr++=np->addrType;
		memcpy(ptr,&np->ipAddress,addrLength(np->addrType));		
		ptr+=addrLength(np->addrType);
		memcpy(ptr, &np->controlPort, 2);
		ptr+=2;

		*ptr++=np->status;
		
		debug("response nbr_request, send neighbor :%s\n", np->userName);
		np=np->next;
	}
	*ptr++=0;
		
	bytes=atomic_out(sock,pdu,ptr-pdu);	

	return 0;
}

int s_route_update(int sock, int nodeID )
{

/*
        +-------+----------+-------+-----+----------+-------+...+---------+----+
        |netType|netAddress| prefix|hops | bandwidth|netType|...|bandwidth|  0 |
        +-------+----------+-------+-----+----------+-------+---+---------+----+
           1         4/6       1      1    2
*/
    unsigned char  netType,prefix,hops;
    int length,bandwidth;
    struct route_item * rip;
    char netAddress[32], ipstr[128];
    
	debug("begin route update\n");
        
        if(nodeID <0){
                sys_log(info_log, "Error: nodeID <0\n");
                return -1;
        }       
        ATOMIC_IN(sock,&netType,1, "read netType error. s_route_update()\n");

        while(netType){
		length=addrLength(netType);
                ATOMIC_IN(sock,netAddress,length,"read NetAddress error. s_route_update()\n");
                ATOMIC_IN(sock,&prefix,1,"read prefix error. s_route_update()\n");
                ATOMIC_IN(sock,&hops,1,"read hops error. s_route_update()\n");
                ATOMIC_IN(sock,&bandwidth,2,"read bandwidth error. s_route_update()\n");

		inet_ntop(netType,netAddress, ipstr, sizeof(ipstr));

		printf("netType=%d length=%d netAddress=.. prefix =%d, hops=%d, bandwidth=%d, nodeID=%d\n\n", 
			netType,length,prefix, hops, ntohs(bandwidth), nodeID);

		//Route Update is very dangerious, ignore it
		//rip=new_route(netType, netAddress, prefix, hops+1, ntohs(bandwidth),nodeID);
		//route_insert(&routes, rip);
		

        	ATOMIC_IN(sock,&netType,1, "read netType error. s_route_update()\n");
        }
        debug("I have got route update from client.\n");
	
	if(f_debug)route_display(&routes);

	return;
}

/**************** Administration command *******************/
/*
	C->S: CMD_ADMIN_SRVC(srvcType:1, action:1); 
	
	
*/
int s_administrator(int client) //, char *OutMsg)
{
	unsigned char buf[2048],srvcType,command,action; 
	struct service *sp;
	unsigned char code;
	int stat=0;

	sp=&neighbors.first->services;

	ATOMIC_IN(client, buf,1, "Read Command  error in s_admin_service");
	command=buf[0];

	switch (command){
	    case SRVC_TYPE_HTTP:
	    case SRVC_TYPE_SOCKS4:
	    case SRVC_TYPE_SOCKS5:
	    {
		/**************************************************************
        	      +----1----+----1------+----1-------+-------1------+
   		C->S: |version:1|CMD_ADMIN:1|SERVICE_TYPE|SERVICE_ACTION|
         	      +---------+-----------+------------+--------------+

   		S->C: |version:1|CMD_ADMIN:1|STATUS|
		****************************************************************/

		 ATOMIC_IN(client, &action,1, "Read srvcType  error in s_admin_service");
		 srvcType=command;

		 if(action==ADMIN_LIST){
		 	stat = service_get_status(sp,srvcType);
			break;
		 } else if(action==ADMIN_PAUSE){
		 	stat = service_set_status(sp,srvcType,SRVC_STAT_PAUSE);
			break;
		 }else if(action==ADMIN_STOP){
		 	stat = service_set_status(sp,srvcType,SRVC_STAT_STOP);
			break;
		 }else if (action==ADMIN_START){
		 	stat = service_get_status(sp,srvcType);
		 	if(stat==SRVC_STAT_START) break; // do nothing;
			else if (stat==SRVC_STAT_PAUSE){
		 		stat = service_set_status(sp,srvcType, SRVC_STAT_START);
				break;
		 	} else if (stat==SRVC_STAT_STOP) {
				stat=start_service(&neighbors.first->services,srvcType);
				break;
		 	}
		        debug("service start ok:%d \n",stat); 
		}//else if 
	   }// case SRVC_XXX
	   break;

	   case ADMIN_NBR :
	   {
		unsigned char u_len,s_len, addrType;
		char userName[256], secret[256],ip_str[128];
		unsigned short ctrlPort;
		in_addr_t ipAddr;
		struct node_item *sp1,*np;
		struct access_list_item4 *acl_item;
		
	/******************************************************************************
        +----1--+----1----+----1----+----1--------+-----1--+----/---+---1----+----/-+-1------+--/---+--2-----+
        |VERSION|CMD_ADMIN|ADMIN_NBR|ADMIN_NBR_ADD|U_LENGTH|userName|S_LENGTH|Secret|addrType|ipAddr|ctrlPort|
        +-------+---------+---------+-------------+--------+--------+--------+------+--------+------+--------+
			     
        *******************************************************************************/
		ATOMIC_IN(client, &action,1,"Read action Error in s_administrator()\n");
		if(action == ADMIN_ADD){
		    ATOMIC_IN(client, &u_len,1,"Read U_LEN Error in s_administrator()\n");
		    ATOMIC_IN(client, userName,u_len, "Read userName error in s_administrator()\n");
		    ATOMIC_IN(client, &s_len,1, "Read S_LEN error in s_administrator()\n");
		    ATOMIC_IN(client, secret , s_len, "Read Secret error in s_administrator()\n");
		    ATOMIC_IN(client, &addrType,1 , "Read AddrType error in s_administrator()\n");
		    ATOMIC_IN(client, &ipAddr, addrLength(addrType), 
			    "Read IP Address error in s_administrator()\n");
		    ATOMIC_IN(client, &ctrlPort, 2, "Read control port error in s_administrator()\n");

 		    np= neighbor_lookup_byName(&neighbors,(char *)userName);
        	    if(np){
                	sys_log(info_log,"Neighbor %s exists already. s_authentication()\n",
				userName);
                	stat=0xFF;
			break;;
        	    }
		
            	    sp1=(struct node_item*) malloc(sizeof(struct node_item) );
            	    if(!sp1) { stat=0xFF; break; }

            	    sp1->ipAddress=ipAddr;  /**** Notice: ipAddress is network address order ***/

            	    //sp1->nodeID=neighbors->number;

		    memset(sp1->userName,0,sizeof(sp1->userName));
            	    strncpy(sp1->userName,userName,u_len);
            	    sp1->controlPort=ctrlPort;  /** network address order**/
            	    sp1->addrType=addrType;

            	    memset(sp1->secret,0,sizeof(sp1->secret));
            	    strncpy(sp1->secret,secret,s_len);

            	    sp1->sourceNode =0;   // comes from local (i.e neighbor[0])

            	    sp1->services.first=NULL;
            	    sp1->services.number=0;
            	    sp1->next=NULL;
		
            	    neighbor_insert(&neighbors,sp1);
		    neighbor_dump(&neighbors);
            	    acl_item=(struct access_list_item4 *) new_acl4(ipAddr,32,ACTION_PERMIT);
            	    if(acl_item!=NULL)
              	    acl_insert4(&acl4, acl_item);

		    //neighbor_display(&neighbors);
		    stat=0;
		    break;
		 }//if(action==ADMIN_ADD)

	   }// case CMD_NBR_ADD

	   break;
	   default:
		 stat= 0xFF;
	   break;
	}//switch 

	buf[0]=VERSION_CTRL;
	buf[1]=CMD_ADMIN;
	buf[2]=stat;
	ATOMIC_OUT(client, buf,3, "send return code error in s_admin_service");

	if(stat ==0xFF || stat ==-1) {
		//strcpy(OutMsg, "Service Not found\n");
		return -1; 
	}
	else{
		//strcpy(OutMsg, "Service Status OK.\n");
		return stat;	
	}
	return 0;
}

/////////////////////
int s_reply_error(int client, unsigned int command){
	
	int bytes;
	PDU_HEADER ph;
	 
        ph.version=VERSION_CTRL;
	ph.command=command;

	bytes=atomic_out(client, &ph, sizeof(ph));
	if(bytes != sizeof(ph)){
		debug(" Error on s_reply_error\n");
		return -1;
	}
	else return 0;
}


